<!DOCTYPE html>
<meta name=viewport content="width=device-width, initial-scale=1">
<meta name=theme-color content="navy">
<meta name=description
  content="BrowsreBox puts you first with the most familiar and secure browser isolation serivce, while our remote and fully isolated cloud browsers protect your network.">
<meta name=author content="BrowsreBox">
<meta name=copyright content="The Dosyago Corporation, Newark, DE">
<meta name=robots content="index,follow">
<title>BrowsreBox &mdash; the Space to Browse Securely.</title>
<style>
  :root {
    min-height: 100%;
    width: 100%;
    user-select: none;
    background: linear-gradient(to top right, navy, black);
  }
  body {
    width: 100%;
    display: table;
    margin: 0 auto;
    height: 100%;
    color: white;
    overflow-x: hidden;
    overflow-y: auto;
    padding-bottom: 25em;
  }
  h2 {
    font-size: 20vmin;
    font-family: system-ui;
    word-break: break-word;
    background-color: lime;
    line-height: 1;
  }
  a {
    display: inline-block;
    margin: 1em 0;
    font-family: Verdana, Helvetica, Arial, sans-serif;
    background: linear-gradient(to right, darkorange, crimson);
  }

  h2 {
    opacity: 0.8;
    margin-bottom: 1rem;
  }

  h3 {
    background: cyan;
    font-size: 10vmin;
    font-family: monospace;
    color: navy;
    text-align: center;
    opacity: 0.7;
    padding-bottom: 0.1em;
    display: table;
    margin: 0 auto;
  }

  h1, h2, a {
    color: navy;
  }
  
  a {
    opacity: 0.9;
    margin-right: 0.25em;
    padding-right: 0.1em;
  }

  h1 {
    opacity: 0.8;
    font-size: 22vmin;
    font-family: monospace;
    word-break: break-all;
    text-align: center;
    text-transform: uppercase;
    line-height: 0.618;
    padding: 0.25em 0 0.5em;
    user-select: text;
    color: darkorange;
    margin-bottom: 0;
  }
   
  h1 i {
    display: inline-block;
    font-style: normal;
    transform: translate(0, -0.5em);
  }

  h1 i:first-of-type {
    transform: translate(-1.125em, 0.5em);
  }
  h1 i:last-of-type {
    transform: translate(1.125em, 0.5em);
  }
</style>
<h2>Browse the web without the web browsing you.</h2>
<div dir=rtl>
  <a href=/evaluation.pdf>View our response to Gartner's Remote Browser Isolation Evaluation Factors.</a>
  <br>
  <a href=mailto:cris@dosyago.com?subject=BrowsreBox>Contact us to discuss an evaluation or deployment.</a>
  <br>
  <a href=https://www.youtube.com/watch?v=SD0Fhl9v87k>Watch the teaser video to see the BrowsreBox in action.</a>
</div>
<h1>BrowsreBox</h1>
<h3>Browse Anything Securely</h3>
<style>
  canvas {
    position: fixed;
    top: -25vh;
    left: -25%;
    width: 150%;
    height: 150vh;
    user-select: none;
    background: transparent;
    z-index: -1;
  }
</style>
<canvas>
  <script>
    const frameReady = () => new Promise(res => window.requestAnimationFrame(res))
    const sleep = ms => new Promise(res => setTimeout(res, ms));
    const ALTERP = 0.618;
    const DTHRESHOLD = 0.618;
    const FRAMEDURATION = 40;
    const F = Math.floor(1000/FRAMEDURATION);
    const UPDATEP = 2;
    const XAVAL = 21;
    const YAVAL = 14;
    const SIZE = [2,2];
    const N = 71;
    const NN = 1;
    const MAXNN = Math.ceil(Math.sqrt(N));
    const MAXD = Math.min(window.innerWidth**2, window.innerHeight**2)/9;
    const C = document.currentScript.parentElement;     
    const S = C.getContext('2d');
    const width = window.innerWidth*2;
    const height = window.innerHeight*2;

    let frameCount = 0;
    let nnds = +1;
    let nn = Math.ceil(Math.sqrt(MAXNN * NN));
    let allPairsCache;

    C.width = width;
    C.height = height;
    S.fillStyle = 'white';
    S.strokeStyle = 'white';
    S.lineWidth =  1;
    S.lineJoin = 'round';
    S.lineCap = 'round';


    self.addEventListener('click', () => {
      nn += nnds;
      if ( nn > MAXNN ) {
        nnds *= -1;
      }
      if ( nn < NN ) {
        nnds *= -1;
      }
    });

    drawLoop();

    async function drawLoop() {
      const Points = createPoints(N);
      while(true) {
        await sleep(FRAMEDURATION);
        await frameReady(); 
        S.clearRect(0,0,width,height);
        update(Points);
        draw(Points); 
        drawTrianglesOnClosest(Points, frameCount);
        frameCount++;
      }
    }

    function update(P) {
      P.forEach(updateA);
    }

    function draw(P) {
      P.forEach(({x,y}) => {
        //S.fillRect(x,y,...SIZE)
        S.beginPath();
        S.arc(x,y,SIZE[0],0,Math.PI*2);
        S.closePath();
        S.fill();
      });
    }

    function drawTrianglesOnClosest(Points, frameNo) {
      const found = new Map();

      if ( ! allPairsCache ) {
        allPairsCache = allPairs(Points)
          //.map(distance);
      }
      allPairsCache
        .map(frameNo % UPDATEP == 0 ? ranDistance: X => X)
        //.map(ranDistance)
        .sort(([{}, {}, d1], [{}, {}, d2]) => d1 - d2)
        .filter(([P1,P2,d]) => {
          const ok = (!found.has(P1) || found.get(P1) <= nn) && (!found.has(P2) || found.get(P2) <= nn); 
          if ( ok ) {
            found.set(P1, (found.get(P1) || 0)+1);
            found.set(P2, (found.get(P2) || 0)+1);
          }
          return ok && d < MAXD;
        })
        .forEach(([P1,P2]) => {
          S.beginPath();
          S.moveTo(P1.x, P1.y);
          S.lineTo(P2.x, P2.y);
          S.stroke();
          S.closePath();
        });

        /**
          .reduce((T, p, i, P) => {
            const last = T[T.length-1];
            if ( last.length == 3 ) {
              T.push([p]);
            } else {
              last.push(p);
            }
          }, []);
        **/
    }

    function updateA(A) {
      const randomX = (Math.random() * XAVAL) - XAVAL/2;
      const randomY = (Math.random() * YAVAL) - YAVAL/2;
      A.x += randomX;
      A.y += randomY;
      return A;
    }

    function createPoints(n) {
      const P = [];
      for( let i = 0; i < n; i++ ) {
        const Point = {
          x: random(width),
          y: random(height),
          V: {
            x: random(width/20),
            y: random(height/20)
          },
          A: updateA({x:XAVAL,y:YAVAL})
        };
        P.unshift(Point);
      }
      return P;
    }

    function random(max) {
      return Math.random() * max;
    }

    function allPairs(A) {
      const P = [];
      A.forEach((a,i) => {
        for( let j = i + 1; j < A.length; j++ ) {
          const [{x:x1,y:y1},{x:X1,y:Y1}] = [a,A[j]];
          P.push([a,A[j],0]);
        }
      });
      return P;
    }

    function distance(P) {
      const [{x:x1,y:y1},{x:X1,y:Y1}] = P;
      const d = ((X1-x1)**2+(Y1-y1)**2);
      P[2] = d;
      return P;
    }
    function ranDistance(P) {
      const [{x:x1,y:y1},{x:X1,y:Y1}] = P;
      const d = ((X1-x1)**2+(Y1-y1)**2);
      if ( Math.random() < ALTERP ) {
        if ( Math.abs(P[2] - d)/d > DTHRESHOLD ) {
          P[2] = d;
        }
      }
      return P;
    }
  </script>
</canvas>
